<?php
class WebService {
	private $requestPatterns = null;
	private $options = null;
	private $defaultHandlers = null;
	
	private $default = array(
		'requestPatterns' => array(
			'methods' => array(
				'word' => 'methods',
				'caseSensitive' => false,
				'listSeparator' => ','
			),
			'invokeType' => array(
				'word' => 'invoketype',
				'caseSensitive' => false,
				'listSeparator' => null
			)
		),
		'defaultHandlers' => array(
			'wsdl'
		),
		'options' => array(
			'specialArgsPattern' => '_',
			'normalizeArgs' => true,
			'normalizationType' => CASE_LOWER
		)
	);
	
	private $reflection;
	
	private $handler = array(
		'class' => null,
		'instance' => null
	);
	
	private $request = null;
	
	public function __construct(
		$handlerClassName = null,
		$handle = true,
		$defaultHandlers = array(),
		$options = array(),
		$requestPatterns = array()
	) {
		$this->reflection = new ReflectionClass(get_class($this));
		
		if (is_string($handlerClassName)) {
				$this->handler['class'] = new ReflectionClass($handlerClassName);
		}
		
		if ((! is_array($defaultHandlers)) || (count($defaultHandlers) == 0)) {
			$this->defaultHandlers = $this->default['defaultHandlers'];
		}
		else {
			$this->defaultHandlers = array_merge(
				$this->default['defaultHandlers'],
				$defaultHandlers
			);
		}
		
		if ((! is_array($options)) || (count($options) == 0)) {
			$this->options = $this->default['options'];
		}
		else {
			$this->options = array_merge(
				$this->default['options'],
				$options
			);
		}
		
		if ((! is_array($requestPatterns)) || (count($requestPatterns) == 0)) {
			$this->requestPatterns = $this->default['requestPatterns'];
		}
		else {
			$this->requestPatterns = array_merge(
				$this->default['requestPatterns'],
				$requestPatterns
			);
		}
		
		$this->parseRequest();
		
		$this->getHandlerInstance($this->request['args']['c']);
		
		if (($handle) || (! is_bool($handle))) {
			$this->handle();
		}
	}
	
	public function getHandlerInstance($constructorArgs = array()) {
		if (($this->handler['instance'] == null) &&
			  ($this->handler['class'] != null)) {
			if (is_array($constructorArgs)) {
				$args = $constructorArgs;
			}
			else {
				$args = array();
			}
			
			$this->handler['instance'] =
				$this->handler['class']->newInstanceArgs($args);
		}
		
		return $this->handler['instance'];
	}
	
	private function normalizeString($string) {
		$strNormalized = null;
		if (array_key_exists('normalizationType', $this->options)) {
			switch ($this->options['normalizationType']) {
				case CASE_LOWER:
					$strNormalized = strtolower($string);
					break;
				case CASE_UPPER:
					$strNormalized = strtoupper($string);
					break;
			}
		}
		
		return $strNormalized;
	}
	
	public function parseRequest() {
		if (count($_REQUEST) > 0) {
			$this->request = array(
				'values' => array_values($_REQUEST),
				'keys' => array(
					'unchanged' => array_flip(array_keys($_REQUEST)),
					'normalized' => array_flip(array_keys(
						array_change_key_case(
							$_REQUEST, 
							$this->options['normalizationType']
						)
					))
				),
				'tokens' => array(),
				'args' => array()
			);
			
			$this->parseRequestTokens();
			
			$this->parseRequestArgs();
		}
	}
	
	private function parseRequestTokens() {
			$matchOrigin = array();
			$patternWord = null;
			
			// GENERATE TOKENS >
			foreach ($this->requestPatterns as $matchKey => $matchData) {
				if ($matchData['caseSensitive']) {
					$matchOrigin[$matchKey] = 'unchanged';
					$patternWord = $matchData['word'];
				}
				else {
					$matchOrigin[$matchKey] = 'normalized';
					$patternWord = $this->normalizeString($matchData['word']);
				}
				
				if (array_key_exists(
					    $patternWord,
							$this->request['keys'][$matchOrigin[$matchKey]]
						)) {
					// list pattern >
					if ((array_key_exists('listSeparator', $matchData)) &&
							(is_string($matchData['listSeparator']))) {
						$this->request['tokens'][$matchKey] = explode(
							$matchData['listSeparator'],
							$this->request
								['values']
								[$this->request
									['keys']
									[$matchOrigin[$matchKey]]
									[$patternWord]
								]
						);
					}
					// < list pattern
					// plain pattern >
					else {
						$this->request['tokens'][$matchKey] = 
							$this->request
								['values']
								[$this->request
									['keys']
									[$matchOrigin[$matchKey]]
									[$patternWord]
								];
					}
					// < plain pattern
				}
				else {
				}
			}
			// < GENERATE TOKENS
	}
	
	private function parseRequestArgs() {
		$specialArg = null;
		$patternWord = null;
		$tokenExist = null;
		
		if ($this->options['normalizeArgs']) {
			$keyMethod = 'normalized';
		}
		else {
			$keyMethod = 'unchanged';
		}
		
		// GENERATE ARGS >
		foreach ($this->request['keys'][$keyMethod] as $key => $valuePosition) {
			$tokenExist = false;
			
			foreach ($this->requestPatterns as $patternKey => $patternData) {
				if (! $patternData['caseSensitive']) {
					switch ($this->options['normalizationType']) {
						case CASE_LOWER:
							$patternWord = strtolower($key);
							break;
						case CASE_UPPER:
							$patternWord = strtoupper($key);
							break;
					}
				}
				else {
					$patternWord = $key;
				}
				
				if ($patternWord == $patternData['word']) {
					$tokenExist = true;
					break;
				}
			}
			
			if (! $tokenExist) {
				if ($patternWord != $patternData['word']) {
					// special arg >
					if (strpos($key, $this->options['specialArgsPattern'])) {
						$specialArg = explode(
							$this->options['specialArgsPattern'],
							$key
						);
						
						if (! array_key_exists($specialArg[0], $this->request['args'])) {
							$this->request['args'][$specialArg[0]] = array();
						}
						
						// array arg >
						if (count($specialArg) >= 3) {
							if (! array_key_exists(
								$specialArg[1],
								$this->request['args'][$specialArg[0]]
							)) {
								$this->request['args'][$specialArg[0]][$specialArg[1]] = array();
							}
							
							$this->request
								['args']
								[$specialArg[0]]
								[$specialArg[1]]
								[implode(
									$this->options['specialArgsPattern'],
									array_slice($specialArg, 2))
								] = $this->request['values'][$valuePosition];
						}
						// < array arg
						// string arg >
						else {
							$this->request
								['args']
								[$specialArg[0]]
								[$specialArg[1]] = $this->request['values'][$valuePosition];
						}
						// < string arg
					}
					// < special arg
					// non special arg >
					else {
						$this->request['args'][$key] =
							$this->request
								['values']
								[$valuePosition];
					}
				}
			}
		}
		// < GENERATE ARGS
	}
	
	public function handle() {
		$handler = null;
		
		if ((is_array($this->request)) && 
			  (array_key_exists('invokeType', $this->request['tokens']))) {
			$handler = $this->reflection->getMethod(
				'handle' .
				'_' .
				strtolower($this->request['tokens']['invokeType'])
			);
			
			if ($handler) {
				$handler->invoke($this);
			}
		}
		else {
			// default handlers
			foreach ($this->defaultHandlers as $index => $handlerName) {
				$handler = $this->reflection->getMethod(
					'handle' .
					'_' .
					strtolower($handlerName)
				);
				
				if ($handler) {
					$handler->invoke($this);
				}
			}
		}
	}
	
	public function handle_wsdl() {
		
		//set ini
		ini_set("soap.wsdl_cache_enabled", 0);
		/*
		ini_set("session.auto_start", 0);
		
		//for persistent session
		session_start();
		*/
		if ((is_array($this->request)) &&
			  (array_key_exists('uri', $this->request['args']))) {
			$uri = $this->request['args']['uri'];
		}
		else {
			$uri = $_SERVER['HTTP_HOST'] . dirname($_SERVER['SCRIPT_NAME']);
		}
		
		$server = new SoapServer(
			null,
			array('uri' => 'urn://' . $uri)
		);
		
		$server->setObject($this->getHandlerInstance());
		//$server->setPersistence(SOAP_PERSISTENCE_SESSION);
		
		$server->handle();
	}
	
	public function handle_param() {
		$handlerInstance = null;
		$handlerMethod = null;
		$handlerClass = $this->handler['class'];
		
		if ((array_key_exists('methods', $this->request['tokens'])) && 
			  ($handlerClass != null)) {
			
			$handlerInstance = $this->getHandlerInstance();
			foreach ($this->request['tokens']['methods'] as $index => $methodName) {
				if ((! array_key_exists($index, $this->request['args'])) ||
					  (! is_array($this->request['args'][$index]))) {
					$args = array();
				}
				else {
					$args = $this->request['args'][$index];
				}
				
				$handlerMethod = $handlerClass->getMethod($methodName);
				
				if ($handlerMethod != null) {
					$handlerMethod->invokeArgs($handlerInstance, $args);
				}
			}
		}
	}
}
?>
